﻿Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
Imports System.Threading.Tasks
Imports System.ComponentModel


'Provides a container object to serve as the state object for async client and stream operations
Public Class ConnectionInfo
    Private _Buffer(512) As Byte
    Public _ipport As String = String.Empty
    Public Event onConnected(ByVal ip_address As String, ByVal account_name As String, ByVal account_id As String)
    Public Event onMessageArrived(ByVal msg As String)
    Private messageBytes As New List(Of Byte)
    Public isConnected As Boolean = True

    Private _Listener As TcpListener
    Public ReadOnly Property Listener As TcpListener
        Get
            Return _Listener
        End Get
    End Property

    Private _Socket As Socket
    Public ReadOnly Property Sock As Socket
        Get
            Return _Socket
        End Get
    End Property

    Private _Client As TcpClient
    Public ReadOnly Property Client As TcpClient
        Get
            Return _Client
        End Get
    End Property

    Private _Stream As NetworkStream
    Public ReadOnly Property Stream As NetworkStream
        Get
            Return _Stream
        End Get
    End Property

    Private _DataQueue As Queue(Of Byte)
    Public ReadOnly Property DataQueue As Queue(Of Byte)
        Get
            Return _DataQueue
        End Get
    End Property

    Private _LastReadLength As Integer
    Public ReadOnly Property LastReadLength As Integer
        Get
            Return _LastReadLength
        End Get
    End Property

    Public Sub New(tcpListener As TcpListener)
        _Listener = tcpListener
        _DataQueue = New Queue(Of Byte)
    End Sub

    Public Sub AcceptClient(result As IAsyncResult)
        _Client = _Listener.EndAcceptTcpClient(result)
        If _Client IsNot Nothing AndAlso _Client.Connected Then
            _Stream = _Client.GetStream
            _ipport = _Client.Client.RemoteEndPoint.ToString
            _Socket = _Client.Client
        End If
    End Sub

    Public Sub AwaitData()
        _Buffer = New Byte(512) {}
        _Stream.BeginRead(_Buffer, 0, _Buffer.Length, AddressOf DoReadData, Me)
    End Sub

    Private Sub DoReadData(result As IAsyncResult)
        Dim info As ConnectionInfo = CType(result.AsyncState, ConnectionInfo)
        Dim tmpMsg As String = String.Empty
        Dim msgSplit As String = String.Empty
        Try
            'If the stream is valid for reading, get the current data and then
            'begin another async read
            If info.Stream IsNot Nothing AndAlso info.Stream.CanRead Then
                info._LastReadLength = info.Stream.EndRead(result)
                If info._LastReadLength > 0 Then
                    For index As Integer = 0 To _LastReadLength - 1
                        info._DataQueue.Enqueue(info._Buffer(index))
                    Next

                    ' Kumpulkan Bytes Dari Buffer
                    While info.DataQueue.Count > 0
                        messageBytes.Add(info.DataQueue.Dequeue)
                    End While

                    tmpMsg = System.Text.Encoding.Unicode.GetString(messageBytes.ToArray)
                    While tmpMsg.IndexOf(Packet.pEof) > -1
                        If tmpMsg.Length > tmpMsg.IndexOf(Packet.pEof) + Packet.pEof.Length Then
                            msgSplit = tmpMsg.Substring(tmpMsg.IndexOf(Packet.pEof) + Packet.pEof.Length)
                            messageBytes.Clear()
                            messageBytes = System.Text.Encoding.Unicode.GetBytes(msgSplit).ToList
                        Else
                            messageBytes.Clear()
                        End If
                        RaiseEvent onMessageArrived(tmpMsg)
                        tmpMsg = System.Text.Encoding.Unicode.GetString(messageBytes.ToArray)
                    End While
                End If

                If info.Client.Connected And info.isConnected Then
                    info.AwaitData()
                Else
                    Exit Sub
                End If
            Else
                'If we cannot read from the stream, the example assumes the connection is
                'invalid and closes the client connection. You might modify this behavior
                'when implementing your own protocol.
                If info.Client.Connected And info.isConnected Then
                    info._LastReadLength = info.Stream.EndRead(result)
                    info.AwaitData()
                Else
                    info._LastReadLength = -1
                End If
            End If
        Catch ex As Exception
            Console.WriteLine("ConnectionModel: " & ex.Message)
            If info.Client.Connected And info.isConnected Then
                info.AwaitData()
            Else
                info._LastReadLength = -1
            End If
        End Try
    End Sub

    Public Sub SendMessage(message As String)
        If _Stream IsNot Nothing Then
            Dim messageData() As Byte = System.Text.Encoding.Unicode.GetBytes(message + Packet.pEof)
            Stream.Write(messageData, 0, messageData.Length)
            Stream.Flush()
        End If
    End Sub

End Class